package com.dduggs.note.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;

import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * 此类描述的是： 用于存储cache 默认有效时间为30分钟
 * @author: theodore.ni
 * @version: 2016年3月2日 下午1:58:54 
 */
@Service("jedisCacheUtil")
public class JedisCacheUtil {

	private static final int LOCK_EXPIRE_SECONDS = 5 * 60;

	private static final int MAX_VALUE_SIZE = 1024 * 1024;

	private static final int LOG_VALUE_SIZE = 50 * 1024;

	private final static Log logger = LogFactory.getLog(JedisCacheUtil.class);

	/** 默认有缓存 失效时间 单位：秒 */
	public final static int DEFAULT_EXPIRE_SECONDS = 60 * 30;
	
	@Autowired
	private ShardedJedisPool shardedJedisPool;

	public void setShardJedisPool(ShardedJedisPool shardedJedisPool) {
		this.shardedJedisPool = shardedJedisPool;
	}

	private ShardedJedisPool getShardJedisPool() {
		return shardedJedisPool;
	}

	/**
	 * 设置 cache 针对字符串 默认有效时间为30分钟
	 * 
	 * @param key
	 * @param value
	 * @return 设置是否成功
	 */
	public boolean setCacheString(String key, String value) {
		return setCacheString(key, value, DEFAULT_EXPIRE_SECONDS);
	}

	/**
	 * 此方法描述的是：设置 cache 针对字符串
	 * @param key
	 * @param value
	 * @param expireSeconds 单位：秒 失效时间
	 * @return 设置是否成功
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:04:39
	 */
	public boolean setCacheString(String key, String value, Integer expireSeconds) {
		if (null == key || "".equals(key.trim())) {
			return false;
		}
		if (null == value || "".equals(value.trim())) {
			return false;
		}
		if (null == expireSeconds) {
			return false;
		}
		if (value.getBytes().length > LOG_VALUE_SIZE) {
			logger.info("redis setCacheString===>" + key + ";value is more than 50KB ");
		}
		if (value.getBytes().length > MAX_VALUE_SIZE) {
			logger.error(key + " value is more than 1M ");
			return false;
		}
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			String isOk = jds.set(key, value);
			if ("ok".equalsIgnoreCase(isOk)) {
				jds.expire(key, expireSeconds);
			}
			return "ok".equalsIgnoreCase(isOk);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return false;
	}

	/**
	 * 此方法描述的是：读取cache中的值，针对 string类型 默认有效时间为30分钟
	 * @param key
	 * @return value 为字符串
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:05:43
	 */
	public String getCacheString(String key) {
		if (null == key || "".equals(key.trim())) {
			return null;
		}
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			return jds.get(key);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return null;
	}

	/**
	 * 此方法描述的是：使用默认失效時間设置cache中的值，针对 object类型
	 * @param key
	 * @param value 序列化的对象
	 * @return 是否设置成功
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:06:09
	 */
	public boolean setCache(String key, Object value) {
		return setCache(key, value, DEFAULT_EXPIRE_SECONDS);
	}

	/**
	 * 此方法描述的是：设置cache中的值，针对 object类型
	 * @param key
	 * @param value 序列化的对象
	 * @param expireSeconds 失效时间 单位：秒
	 * @return 是否设置成功
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:06:29
	 */
	public boolean setCache(String key, Object value, Integer expireSeconds) {
		if (null == key || "".equals(key.trim())) {
			return false;
		}
		if (null == expireSeconds) {
			return false;
		}
		if (null == value) {
			return false;
		}
		byte[] serializeValue = SerializeUtil.serialize(value);
		if (serializeValue.length > LOG_VALUE_SIZE) {
			logger.info("redis setCache===>" + key + ";value is more than 50KB ");
		}
		if (serializeValue.length > MAX_VALUE_SIZE) {
			logger.error(key + " value is more than 1M ");
			return false;
		}
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			byte[] bytes = key.getBytes();
			String isOk = jds.set(bytes, serializeValue);
			if(expireSeconds == -1){
				return "ok".equalsIgnoreCase(isOk);
			}
			if ("ok".equalsIgnoreCase(isOk)) {
				jds.expire(bytes, expireSeconds);
			}
			return "ok".equalsIgnoreCase(isOk);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return false;
	}

	/**
	 * 此方法描述的是：读取cache中的值，针对 object类型
	 * @param key
	 * @return 已反序列化好的对象
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:06:51
	 */
	public Object getCache(String key) {
		if (null == key || "".equals(key.trim())) {
			return null;
		}
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			byte[] bytes = jds.get(key.getBytes());
			if (null != bytes) {
				return SerializeUtil.unserialize(bytes);
			}
			return null;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return null;
	}

	/**
	 * 获取HashMap中所有的key值
	 * @param key redis key
	 * @return 返回map中所有的key
     */
	public String getSetValue(String key, String field){
		if (StringUtils.isEmpty(key)) {
			return null;
		}
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			return jds.hget(key, field);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}finally {
			if(jds != null)
				jds.close();
		}
		return null;
	}

	/**
	 * 此方法描述的是：删除string类型 的key ,请使用 deleteCacheStringKey 方法
	 * @param key
	 * @return 被删除key 的数量
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:14:22
	 */
	private Long deleteKey(String key) {
		if (null == key || "".equals(key.trim())) {
			return 0L;
		}
		
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			return jds.del(key);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if(jds != null) {
				jds.close();
			}
		}
		return 0L;
	}

	/**
	 * 此方法描述的是：删除 setCacheString 这样方法设置的string key
	 * @param key
	 * @return
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:14:45
	 */
	public Long deleteCacheStringKey(String key) {
		return deleteKey(key);
	}

	/**
	 * 此方法描述的是：将key值转为byte[]后再删除 byte[] 这样的key 删除 setCache 这样方法设置的string key
	 * @param key
	 * @return
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:14:58
	 */
	private Long deleteStrToBytesKey(String key) {
		if (null == key || "".equals(key.trim())) {
			return 0L;
		}
		
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			return jds.del(key.getBytes());
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if(jds != null) {
				jds.close();
			}
		}
		return 0L;
	}

	/**
	 * 此方法描述的是：删除 setCache 这样方法设置的string key
	 * @param key
	 * @return
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:15:26
	 */
	public Long deleteCacheKey(String key) {
		return deleteStrToBytesKey(key);
	}

	/**
	 * 此方法描述的是：获取key的有效期时间 ：当key 不存在时，返回-2 当key 存在但没有设置剩余生存时间时，返回-1 。 否则，以秒为单位，返回key
	 * 的剩余生存时间。
	 * @param key
	 * @return
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:15:47
	 */
	public Long getKeyExpire(String key) {
		if (null == key || "".equals(key.trim())) {
			return 0L;
		}
		
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			return jds.ttl(key);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return 0L;
	}

	/**
	 * 此方法描述的是：设置此值时，key必须存在并且key在有效期内，否则 无效
	 * @param key
	 * @param expireSeconds
	 * @return
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:11:41
	 */
	public Long setKeyExpire(String key, int expireSeconds) {
		if (null == key || "".equals(key.trim())) {
			return null;
		}
		
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			return jds.expire(key, expireSeconds);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return null;
	}

	/**
	 * 此方法描述的是：判断此key是否存在
	 * @param key
	 * @return
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:11:27
	 */
	public boolean keyExists(String key) {
		if (null == key || "".equals(key.trim())) {
			return false;
		}
		
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			return jds.exists(key);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return false;
	}

	/**
	 * 此方法描述的是：取得有效时间内的锁
	 * @param key
	 * @param expireSeconds 有效时间 单位 秒
	 * @return
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:11:09
	 */
	public boolean lock(String key, int expireSeconds) {
		if (null == key || "".equals(key.trim())) {
			return false;
		}
		if (expireSeconds == 0) {
			expireSeconds = LOCK_EXPIRE_SECONDS;
		}
		
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			Long i = jds.setnx(key, System.currentTimeMillis() + "");
			if (i.intValue() == 1) {
				jds.expire(key, expireSeconds);
				return true;
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return false;
	}

	/**
	 * 此方法描述的是：获取锁,默认锁定五分钟
	 * 
	 * @param key
	 * @return
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:10:51
	 */
	public boolean lock(String key) {
		return this.lock(key, LOCK_EXPIRE_SECONDS);
	}

	/**
	 * 此方法描述的是：释放锁
	 * @param key
	 * @return
	 * @author: theodore.ni
	 * @version: 2016年3月2日 下午2:10:34
	 */
	public boolean unLock(String key) {
		if (null == key || "".equals(key.trim())) {
			return false;
		}
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			Long i = jds.del(key);
			if (i.intValue() == 1) {
				return true;
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return false;
	}

	/**
	 * 返回名称为key的hash中所有键对应的value
	 *
	 * @param key
	 * @return
     */
	public List<String> hvals(String key){
		if (StringUtils.isEmpty(key)) {
			return null;
		}
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			return jds.hvals(key);
		}catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return null;
	}

	/**
	 * 向名称为key的hash中添加元素field<—>value
	 *
	 * @param key key
	 * @param field field
	 * @param value 值
     * @return 是否成功
     */
	public boolean hset(String key, String field, String value){
		if (null == key || "".equals(key.trim())) {
			return false;
		}
		if (null == field || "".equals(field.trim())) {
			return false;
		}
		if (null == value) {
			return false;
		}
		if (value.getBytes().length > LOG_VALUE_SIZE) {
			logger.info("redis setCacheString===>" + key + ";value is more than 50KB ");
		}
		if (value.getBytes().length > MAX_VALUE_SIZE) {
			logger.error(key + " value is more than 1M ");
			return false;
		}
		ShardedJedis jds = null;
		try {
			jds = getShardJedisPool().getResource();
			jds.hset(key, field, value);
			return true;
		} catch (Exception e){
			logger.error(e.getMessage(), e);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return false;
	}

	/**
	 * 自增缓存键中的值.
	 * @param key redis key
	 * @return 操作后的值
	 */
	public Long incr(String key) {
		ShardedJedis jds = null;
		Long incr = null;
		try {
			jds = getShardJedisPool().getResource();
			incr = jds.incr(key);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}finally {
			if (jds != null) {
				jds.close();
			}
		}
		return incr;
	}

	/**
	 * 对redis中的key设置过期时间点
	 * @param key key
	 * @param unixTime 过期时间点
     * @return 1成功，0失败
     */
	public Long expireAt(String key, long unixTime) {
		ShardedJedis jds = null;
		Long r = null;
		try {
			jds = getShardJedisPool().getResource();
			r = jds.expireAt(key, unixTime);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		}finally {
			if (jds != null) {
				jds.close();
			}
		}
		return r;
	}

	/**
	 * 存储list数据
	 * @param key key
	 * @param member 数据
     * @return 1 if the new element was added 0 if the element was
	 *         already a member of the set
     */
	public Long lpush(String key, String... member) {
		ShardedJedis jds = null;
		Long r = null;
		try {
			jds = getShardJedisPool().getResource();
			r = jds.lpush(key, member);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return r;
	}

	public String rpop(String key) {
		ShardedJedis jds = null;
		String value = null;
		try {
			jds = getShardJedisPool().getResource();
			value = jds.rpop(key);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		} finally {
			if (jds != null) {
				jds.close();
			}
		}
		return value;
	}

	/**
	 * 获取redis key
	 * @param keys
	 * @return
     */
	public Set<String> keys(String keys){
		ShardedJedis jedis = null;
		Set<String> value = null;
		try {
			jedis = getShardJedisPool().getResource();
			value = jedis.hkeys(keys);//HLEN
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
		return value;
	}

	public Long hlen(String key){
		ShardedJedis jedis = null;
		Long value = null;
		try {
			jedis = getShardJedisPool().getResource();
			value = jedis.hlen(key);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
		return value;
	}

	public Map<String, String> hgetAll(String key){
		ShardedJedis jedis = null;
		Map<String, String> value = null;
		try {
			jedis = getShardJedisPool().getResource();
			value = jedis.hgetAll(key);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
		return value;
	}
}